from django.shortcuts import render

# Create your views here.
from rest_framework.viewsets import GenericViewSet
from .serializers import UserSerializer
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import User

from rest_framework_simplejwt.tokens import RefreshToken
from user_app.utils.redis_utils import RedisClient
#登录
class LoginViewSet(GenericViewSet):
    #先获取用户手机号和密码登录
    '''

 - serializer_class 指明视图使用的序列化器
- queryset 指明使用的数据查询集

### 方法

- get_serializer_class(self) 返回序列化器类
- get_serializer(self, args, *kwargs) 返回序列化器对象
- get_queryset(self) 返回视图使用的查询集
- get_object(self) 返回视图所需的模型类数据对象
    '''
    serializer_class = UserSerializer
    def create(self, request, *args, **kwargs):
        """
        登录接口
        """
        # 调用序列化器类
        serializer = self.get_serializer(data=request.data)#request.data是表单也就是前端返回的数据
        # # 这里可以添加实际的登录验证逻辑
        if not serializer.is_valid():
            return Response(serializer.errors, status=400)
        # 验证成功后，获取用户手机和密码
        phone = serializer.validated_data.get('phone')
        password = serializer.validated_data.get('password')
        #获取用户id和nick_name
        user = User.objects.filter(phone=phone).first() 
        '''
        #显式调用加密，user.password会重定向到_password属性让user的_password存储加密后的密码，
        # 不能用user.password(password)这是函数调用语法，但password是属性不是方法
   
        '''       
        user.password = user._password#把数据库的密码赋值给user.password，也就是调用setter方法，存储加密后的密码到_password中


        # 验证手机号和密码逻辑
        if not phone or not password:
            return Response({"error": "手机号和密码不能为空"}, status=400)
        #调用models.py中的User类中的check_password方法，传入的事用户表单输入的password，来和数据库的password进行对比
        if user and user.check_password(password):#这里要用user来调用而不是User类名调用，因为会缺少self参数
            #登录成功,返回token，code，user_id,nickname
            '''
            使用双 Token（Access + Refresh）的好处：
            Access Token 生命周期短，即使泄露影响也小
            Refresh Token 可以存储在更安全的地方（如 HttpOnly Cookie）
            当用户登出时，只需将 Refresh Token 加入黑名单即可
            '''
            refresh = RefreshToken.for_user(user)#用于生成长期的 Refresh Token，也就是用来刷新短效的access_token
            access_token = str(refresh.access_token)
            refresh_token = str(refresh)

            # 获取 jti 和过期时间
            jti = refresh.access_token.payload['jti']
            exp = refresh.access_token.payload['exp']

            # 将 jti 和过期时间存入 Redis
            
            RedisClient().set_access_token(jti, exp,user_id=user.id)
            # 返回响应
            response = Response({
            "access": access_token,
            "refresh": refresh_token,
            "user_id": user.id,
            "nickname": user.nickname,
            "msg":'登录成功'
        }, status=200)

            # 设置 HttpOnly Cookie 存储 refresh token
            response.set_cookie(
                key='refresh_token',
                value=refresh_token,
                httponly=True,#存储在 HttpOnly Cookie 中，无法通过 JavaScript 访问，防止 XSS 攻击。
                secure=True, # 仅通过 HTTPS 协议传输（防止中间人窃取）
                samesite='Strict'# 防止跨站请求伪造（CSRF）：仅同源请求才会带上这个 Cookie
            )
            return response


        else:
            return Response({"error": "手机号或密码错误"}, status=400)


        
       
        
    


#刷新token接口

class RefreshTokenView(APIView):
    def post(self, request):
        refresh_token = request.COOKIES.get('refresh_token')
        if not refresh_token:
            return Response({"error": "Refresh Token 不存在"}, status=400)#过期了满了就得重新登陆不能继续续期access来续期了

        try:
            refresh = RefreshToken(refresh_token)#用已有 Refresh Token 生成新的 Access Token
            '''
            每次调用 refresh.access_token，都会生成一个新的 Access Token，有效期重新计算。
            这样可以防止用户长时间不操作仍保持登录状态。
            '''
            new_access_token = str(refresh.access_token)
            '''
            refresh.access_token.payload 是 新生成的 access token 解析后的数据结构；
            它包含：
            jti: JWT ID，唯一标识这个 token；
            exp: 过期时间戳；
            user_id: 用户 ID，用于识别是谁的 token；
            这些字段是 SimpleJWT 自动生成的，你不需要手动存入；
            所以你可以直接从 payload 中拿到这些信息
                        '''
            # 更新 Redis 中的 Access Token
            jti = refresh.access_token.payload['jti']
            exp = refresh.access_token.payload['exp']
            # 获取用户信息
            user = User.objects.filter(id=refresh.access_token.payload['user_id']).first()
            RedisClient().set_access_token(jti, exp,user_id=user.id)
            

            return Response({"access": new_access_token}, status=200)
        except Exception as e:
            return Response({"error": "Refresh Token 失效或无效"}, status=400)
        


# 登出接口

class LogoutView(GenericViewSet):
    def create(self, request):
        refresh_token = request.COOKIES.get('refresh_token')
        response = Response({"message": "已登出"}, status=200)

        if refresh_token:
            try:
                refresh = RefreshToken(refresh_token)
                refresh.blacklist()  # 加入黑名单

                # 删除 Redis 中的 Access Token
                jti = refresh.access_token.payload['jti']
                RedisClient().delete_access_token(jti)
            except Exception:
                pass

        response.delete_cookie('refresh_token')  # 删除 Cookie
        return response




